/*
 * File      : fh_aes.c
 *
 * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd.
 * All rights reserved
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Visit http://www.fullhan.com to get contact with Fullhan.
 *
 * Change Logs:
 * Date           Author       Notes
 */


/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/

#include "fh_aes.h"
#include <asm/arch/hardware.h>
/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/
//#define AES_DEBUG
#ifdef AES_DEBUG
#define AES_DBG(fmt, args...)     \
    do                                  \
    {                                   \
        printf("[AES]: ");   \
        printf(fmt, ## args);       \
    }                                   \
    while(0)
#else
#define AES_DBG(fmt, args...)  do { } while (0)
#endif

#define AES_ERR(fmt, args...)     \
    do                                  \
    {                                   \
        printf("[AES ERROR]: ");   \
        printf(fmt, ## args);       \
    }                                   \
    while(0)

#define CRYPTO_QUEUE_LEN    (1)
#define CRYPTION_POS		(0)
#define METHOD_POS			(1)
#define EMODE_POS			(4)
#define EFUSE_TIMEOUT			100000

#define __raw_writeb(v,a)       (*(volatile unsigned char  *)(a) = (v))
#define __raw_writew(v,a)       (*(volatile unsigned short *)(a) = (v))
#define __raw_writel(v,a)       (*(volatile unsigned int   *)(a) = (v))

#define __raw_readb(a)          (*(volatile unsigned char  *)(a))
#define __raw_readw(a)          (*(volatile unsigned short *)(a))
#define __raw_readl(a)          (*(volatile unsigned int   *)(a))

#define aes_readl(aes, name) \
	__raw_readl(&(((struct fh_aes_reg *)aes->regs)->name))

#define aes_writel(aes, name, val) \
	__raw_writel((val), &(((struct fh_aes_reg *)aes->regs)->name))

#define aes_readw(aes, name) \
	__raw_readw(&(((struct fh_aes_reg *)aes->regs)->name))

#define aes_writew(aes, name, val) \
	__raw_writew((val), &(((struct fh_aes_reg *)aes->regs)->name))

#define aes_readb(aes, name) \
	__raw_readb(&(((struct fh_aes_reg *)aes->regs)->name))

#define aes_writeb(aes, name, val) \
	__raw_writeb((val), &(((struct fh_aes_reg *)aes->regs)->name))



#define wrap_readl(wrap, name) \
	__raw_readl(&(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_writel(wrap, name, val) \
	__raw_writel((val), &(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_readw(wrap, name) \
	__raw_readw(&(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_writew(wrap, name, val) \
	__raw_writew((val), &(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_readb(wrap, name) \
	__raw_readb(&(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_writeb(wrap, name, val) \
	__raw_writeb((val), &(((struct wrap_efuse_reg *)wrap->regs)->name))



#define ASSERT( expr) if( !( expr ) ) { \
		printf( "Assertion failed! %s:line %d \n", \
		__FUNCTION__,__LINE__); \
		while(1);\
		}

#define DMA_MAX_PROCESS_SIZE            2048


#define AES_SUPPORT_FLAG       (CRYPTO_CPU_SET_KEY | CRYPTO_EX_MEM_SET_KEY |\
                        CRYPTO_EX_MEM_SWITCH_KEY | CRYPTO_EX_MEM_4_ENTRY_1_KEY | CRYPTO_EX_MEM_INDEP_POWER)

#define MATCH_KEY 0x92fc0025


#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define SECURE_BOOT_ENTRY       (48)
#define SECURE_BOOT_ACTIVE_VAL  (1<<0)
#define SOC_BOOT_MODE_WITH_SECURE   0xaa5555aa
#define SECURE_PROTECT_EFUSE_ENTRY_MAX_INDEX    16
struct wrap_efuse_reg
{
    u32 efuse_cmd;              /* 0x0 */
    u32 efuse_config;           /* 0x4 */
    u32 efuse_match_key;        /* 0x8 */
    u32 efuse_timing0;          /* 0xc */
    u32 efuse_timing1;          /* 0x10 */
    u32 efuse_timing2;          /* 0x14 */
    u32 efuse_timing3;          /* 0x18 */
    u32 efuse_timing4;          /* 0x1c */
    u32 efuse_timing5;          /* 0x20 */
    u32 efuse_timing6;          /* 0x24 */
    u32 efuse_dout;             /* 0x28 */
    u32 efuse_status0;          /* 0x2c */
    u32 efuse_status1;          /* 0x30 */
    u32 efuse_status2;          /* 0x34 */
    u32 efuse_status3;          /* 0x38 */
    u32 efuse_status4;          /* 0x3c */

    u32 efuse_reserve1[24];     /* 0x40 ~ 9C */
    u32 efuse_debug_config;     /* 0xa0 */
    u32 efuse_debug_match_key;  /* 0xa4 */
    u32 efuse_debug_status0;    /* 0xa8 */
    u32 efuse_debug_status1;    /* 0xac */
    u32 efuse_debug_status2;    /* 0xb0 */
    u32 efuse_debug_status3;    /* 0xb4 */
    u32 efuse_debug_status4;    /* 0xb8 */
    u32 efuse_debug_status5;    /* 0xbc */
    u32 efuse_debug_status6;    /* 0xc0 */
    u32 efuse_reserve2[15];     /* 0xc4 ~ 0xfc */

    u32 efuse_mem_info;         /* 0x100 ~ 0x1fc */
};

struct wrap_efuse_obj s_efuse_obj = {0};
struct fh_aes_driver g_aes_driver = {0};

#define EFUSE_MAX_ENTRY         60

/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type that only be used in this file  here
 ***************************************************************************/
enum{
	CMD_TRANS_AESKEY = 4,
	CMD_WFLGA_AUTO = 8,
};


enum {
	ENCRYPT = 0 << CRYPTION_POS,
	DECRYPT = 1 << CRYPTION_POS,
};

enum {
	ECB_MODE = 0 << EMODE_POS,
	CBC_MODE = 1 << EMODE_POS,
	CTR_MODE = 2 << EMODE_POS,
	CFB_MODE = 4 << EMODE_POS,
	OFB_MODE = 5 << EMODE_POS,
};

enum {
	DES_METHOD = 0 << METHOD_POS,
	TRIPLE_DES_METHOD = 1 << METHOD_POS,
	AES_128_METHOD = 4 << METHOD_POS,
	AES_192_METHOD = 5 << METHOD_POS,
	AES_256_METHOD = 6 << METHOD_POS,
};


#define EFUSE_ERR_WERR			1<<0
#define EFUSE_ERR_RERR			1<<1
#define EFUSE_ERR_AESERR		1<<2
#define EFUSE_ERR_CMDERR		1<<3
#define EFUSE_ERR_OPERR			1<<4
#define EFUSE_ERR_MATCHERR		1<<5
#define EFUSE_ERR_READLOCK		1<<6




/*****************************************************************************

 *  static fun;
 *****************************************************************************/
static int fh_aes_set_key(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p);
static void fh_set_aes_key_reg(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p);
#ifdef CONFIG_CRYPTO_AES_ECB_SUPPORT
static int fh_aes_encrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p);
static int fh_aes_decrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_AES_CBC_SUPPORT
static int fh_aes_cbc_encrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p);
static int fh_aes_cbc_decrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_AES_CTR_SUPPORT
static int fh_aes_ctr_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_aes_ctr_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_AES_OFB_SUPPORT
static int fh_aes_ofb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_aes_ofb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_AES_CFB_SUPPORT
static int fh_aes_cfb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_aes_cfb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_ECB_SUPPORT
static int fh_des_ecb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_ecb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_CBC_SUPPORT
static int fh_des_cbc_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_cbc_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_OFB_SUPPORT
static int fh_des_ofb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_ofb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_CFB_SUPPORT
static int fh_des_cfb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_cfb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_ECB_SUPPORT
static int fh_des_tri_ecb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_tri_ecb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_CBC_SUPPORT
static int fh_des_tri_cbc_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_tri_cbc_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_OFB_SUPPORT
static int fh_des_tri_ofb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_tri_ofb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_CFB_SUPPORT
static int fh_des_tri_cfb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
static int fh_des_tri_cfb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p);
#endif
static void aes_biglittle_swap(u8 *buf);
void efuse_trans_key(struct fh_aes_driver *p_driver,struct rt_crypto_request *request_p);
void open_efuse_power(struct wrap_efuse_obj *obj);
void efuse_buffer_set(struct wrap_efuse_obj *obj, unsigned int value);
void refresh_efuse(struct wrap_efuse_obj *obj);
static int get_soc_crypto_load_mode_from_efuse(struct rt_crypto_obj *obj_p);
/*****************************************************************************
 * Global variables section - Local
 * define global variables(will be refered only in this file) here,
 * static keyword should be used to limit scope of local variable to this file
 * e.g.
 *  static uint8_t ufoo;
 *****************************************************************************/

struct rt_crypto_obj crypto_table[] =  {
#ifdef CONFIG_CRYPTO_AES_ECB_SUPPORT
		{
				.name = "aes-ecb",
				.flag = FALSE,
				.block_size = 16,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = AES_MAX_KEY_SIZE,
				.min_key_size = AES_MIN_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_aes_encrypt,
				.ops.decrypt = fh_aes_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_AES_CBC_SUPPORT
		{
				.name = "aes-cbc",
				.flag = TRUE,
				.block_size = 16,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = AES_MAX_KEY_SIZE,
				.min_key_size = AES_MIN_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_aes_cbc_encrypt,
				.ops.decrypt = fh_aes_cbc_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_AES_CTR_SUPPORT
		{
				.name = "aes-ctr",
				.flag = TRUE,
				.block_size = 16,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = AES_MAX_KEY_SIZE,
				.min_key_size = AES_MIN_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_aes_ctr_encrypt,
				.ops.decrypt = fh_aes_ctr_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_AES_OFB_SUPPORT
		{
				.name = "aes-ofb",
				.flag = TRUE,
				.block_size = 16,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = AES_MAX_KEY_SIZE,
				.min_key_size = AES_MIN_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_aes_ofb_encrypt,
				.ops.decrypt = fh_aes_ofb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_AES_CFB_SUPPORT
		{
				.name = "aes-cfb",
				.flag = TRUE,
				.block_size = 16,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = AES_MAX_KEY_SIZE,
				.min_key_size = AES_MIN_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_aes_cfb_encrypt,
				.ops.decrypt = fh_aes_cfb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_ECB_SUPPORT
		{
				.name = "des-ecb",
				.flag = FALSE,
				.block_size = 16,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES_KEY_SIZE,
				.min_key_size = DES_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_ecb_encrypt,
				.ops.decrypt = fh_des_ecb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_CBC_SUPPORT
		{
				.name = "des-cbc",
				.flag = TRUE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES_KEY_SIZE,
				.min_key_size = DES_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_cbc_encrypt,
				.ops.decrypt = fh_des_cbc_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_OFB_SUPPORT
		{
				.name = "des-ofb",
				.flag = TRUE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES_KEY_SIZE,
				.min_key_size = DES_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_ofb_encrypt,
				.ops.decrypt = fh_des_ofb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_CFB_SUPPORT
		{
				.name = "des-cfb",
				.flag = TRUE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES_KEY_SIZE,
				.min_key_size = DES_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_cfb_encrypt,
				.ops.decrypt = fh_des_cfb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_ECB_SUPPORT
		{
				.name = "des3-ecb",
				.flag = FALSE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES3_EDE_KEY_SIZE,
				.min_key_size = DES3_EDE_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_tri_ecb_encrypt,
				.ops.decrypt = fh_des_tri_ecb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_CBC_SUPPORT
		{
				.name = "des3-cbc",
				.flag = TRUE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES3_EDE_KEY_SIZE,
				.min_key_size = DES3_EDE_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_tri_cbc_encrypt,
				.ops.decrypt = fh_des_tri_cbc_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_OFB_SUPPORT
		{
				.name = "des3-ofb",
				.flag = TRUE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES3_EDE_KEY_SIZE,
				.min_key_size = DES3_EDE_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_tri_ofb_encrypt,
				.ops.decrypt = fh_des_tri_ofb_decrypt,
		},
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_CFB_SUPPORT
		{
				.name = "des3-cfb",
				.flag = TRUE,
				.block_size = 8,
				.src_allign_size = 4,
				.dst_allign_size = 4,
				.max_key_size = DES3_EDE_KEY_SIZE,
				.min_key_size = DES3_EDE_KEY_SIZE,
				.otp_size = DMA_MAX_PROCESS_SIZE,
				.ops.set_key =fh_aes_set_key,
				.ops.encrypt = fh_des_tri_cfb_encrypt,
				.ops.decrypt = fh_des_tri_cfb_decrypt,
		},
#endif
};

/*  0      :means do not use secure boot or decrypt. all efuse could be transfered to aes
    others :means use secure boot mode. efuse entry 0~15 DO NOT USE
*/
int secure_check(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p){
    struct fh_aes_driver *p_driver;
    int ret;
    int i, key_map_size;
    struct ex_key_map *p_key_map;

    p_driver = (struct fh_aes_driver *) obj_p->driver_private;

    ret = get_soc_crypto_load_mode_from_efuse(obj_p);
    if(ret != SOC_BOOT_MODE_WITH_SECURE)
        return 0;
    if(request_p->control_mode & DECRYPT)
        return 0;
    
    if(p_driver->open_flag & CRYPTO_EX_MEM_SWITCH_KEY){
        /*chip need set the map...and usr set ~~~*/
        if(request_p->key_flag & CRYPTO_EX_MEM_SWITCH_KEY){
            //parse efuse map..
            if(request_p->key_flag & CRYPTO_EX_MEM_4_ENTRY_1_KEY){
                key_map_size = request_p->adv.ex_key_para.map_size;
                p_key_map = &request_p->adv.ex_key_para.map[0];
                ASSERT(key_map_size <= MAX_EX_KEY_MAP_SIZE);
                efuse_check_map_para(key_map_size,p_key_map);
                for (i = 0; i < key_map_size; i++,p_key_map++)
                {
                    if(p_key_map->ex_mem_entry < SECURE_PROTECT_EFUSE_ENTRY_MAX_INDEX){
                        printf("Do not use secure area\n");
                        return -1;
                    }
     
                }
            }
        }
        else{
            /* efuse 0 ~ 15 must be used*/
            return -2;
        }
    }
    else{
        /* efuse 0 ~ 15 must be used*/
        return -3;
    }
    return 0;
}

static void fh_set_aes_key_reg(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;
    int i;
    u32 method;
    u8 *key;
    u32 temp_key_buf[32];
    u32 temp_iv_buf[32];
    u32 *p_dst = NULL;
    u32 key_size = 0;
    key = request_p->key;
    p_driver = (struct fh_aes_driver *) obj_p->driver_private;
    if (request_p->iv_flag == TRUE)
    {
        /* set iv */
        /* if aes mode ....set 128 bit iv,  des set 64bit iv.. */
        AES_DBG("set iv reg\n");
        if ((request_p->control_mode & AES_128_METHOD)
                || ((request_p->control_mode & AES_192_METHOD))
                || (request_p->control_mode & AES_256_METHOD))
        {
            ASSERT(request_p->iv != NULL);
            AES_DBG("aes iv mode...\n");
            memcpy((u8 *) &temp_iv_buf[0], request_p->iv, 16);
            p_dst = &temp_iv_buf[0];
            for (i = 0; i < 16 / sizeof(u32); i++)
            {
                aes_biglittle_swap((u8 *) (p_dst + i));

            }
            memcpy((u8 *) &((struct fh_aes_reg *) p_driver->regs)->initial_vector0,
                    temp_iv_buf, 16);

        }
        else
        {
            AES_DBG("des iv mode...\n");

            memcpy((u8 *) &temp_iv_buf[0], request_p->iv, 8);
            p_dst = &temp_iv_buf[0];
            for (i = 0; i < 8 / sizeof(u32); i++)
            {
                aes_biglittle_swap((u8 *) (p_dst + i));
            }
            memcpy((u8 *) &((struct fh_aes_reg *) p_driver->regs)->initial_vector0,
                    temp_iv_buf, 8);

        }
    }
    /* set key... */
    method = request_p->control_mode & 0x0e;
    AES_DBG("set key reg\n");
    switch (method)
    {
    case AES_128_METHOD:
        AES_DBG("set key aes 128 mode..\n");

        key_size = 16;
        break;
    case AES_192_METHOD:
        AES_DBG("set key aes 192 mode..\n");

        key_size = 24;
        break;

    case AES_256_METHOD:
        AES_DBG("set key aes 256 mode..\n");

        key_size = 32;

        break;

    case DES_METHOD:
        AES_DBG("set key des normal mode..\n");

        key_size = 8;
        break;

    case TRIPLE_DES_METHOD:
        AES_DBG("set key des triple mode..\n");

        key_size = 24;

        break;

    default:
        AES_DBG("error method!!\n");
        break;
    }
    ASSERT(key_size == request_p->key_size);
    if ((request_p->key_flag & CRYPTO_EX_MEM_SET_KEY) &&
    (p_driver->open_flag & CRYPTO_EX_MEM_SET_KEY))
    {
        AES_DBG("use ex mem transkey..\n");
        if(secure_check(obj_p, request_p)){
            printf("secure check failed..\n");
            return;
        }
        efuse_trans_key(p_driver,request_p);
    }
    else
    {
        AES_DBG("use cpu transkey..\n");
        memcpy((u8 *) &temp_key_buf[0], key, key_size);
        p_dst = &temp_key_buf[0];
        for (i = 0; i < key_size / sizeof(u32); i++)
        {
            aes_biglittle_swap((u8 *) (p_dst + i));

        }
        memcpy((u8 *) &((struct fh_aes_reg *) p_driver->regs)->security_key0, temp_key_buf,
                key_size);
    }
}



static void aes_biglittle_swap(u8 *buf) {
	u8 tmp, tmp1;
	tmp = buf[0];
	tmp1 = buf[1];
	buf[0] = buf[3];
	buf[1] = buf[2];
	buf[2] = tmp1;
	buf[3] = tmp;
}

static int fh_set_indata(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p){

	return 0;

}

static int fh_set_outdata(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p){

	return 0;
}

static void fh_unset_indata(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p) {

}

static void fh_unset_outdata(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p) {

}


static void fh_set_dma_indata(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p) {
	struct fh_aes_driver *p_driver;
	p_driver = (struct fh_aes_driver *)obj_p->driver_private;
	aes_writel(p_driver, dma_src_add,(u32)request_p->data_src);
	AES_DBG("dma indata add is :%x\n", (u32)request_p->data_src);

	aes_writel(p_driver, dma_trans_size, request_p->data_size);
	AES_DBG("dma trans size is :%x\n", request_p->data_size);
}

static void fh_set_dma_outdata(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p) {
	struct fh_aes_driver *p_driver;
	p_driver = (struct fh_aes_driver *)obj_p->driver_private;
	aes_writel(p_driver, dma_dst_add, (u32)request_p->data_dst);
	AES_DBG("dma outdata add is :%x\n", (u32)request_p->data_dst);
	aes_writel(p_driver, dma_trans_size, request_p->data_size);
	AES_DBG("dma trans size is :%x\n", request_p->data_size);
}

static void fh_aes_update_request_data_size(struct rt_crypto_request *request_p,
u32 max_xfer_size, u32 *total_size, u32 *first)
{
    u32 step_size = 0;

    step_size = MIN(max_xfer_size, *total_size);
    if (*first == 0)
    {
        *first = 1;
    }
    *total_size -= step_size;
    request_p->data_size = step_size;
}

static void fh_aes_update_request_data_add(struct rt_crypto_request *request_p)
{
    request_p->data_src += request_p->data_size;
    request_p->data_dst += request_p->data_size;
}

static void fh_aes_handle_request(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{

    struct fh_aes_driver *p_driver;
    u32 *mode;
    u32 outfifo_thold;
    u32 infifo_thold;
    u32 isr;
    u32 ret;
    u32 total_size;
    int isr_status;
    u32 first_in = 0;
    int err;
    p_driver = (struct fh_aes_driver *) obj_p->driver_private;


    total_size = request_p->data_size;
    if(!(request_p->key_flag <= p_driver->open_flag)){
        AES_ERR("request flag : %x; driver flag:%x\n",request_p->key_flag,p_driver->open_flag);
        ASSERT(request_p->key_flag <= p_driver->open_flag);
    }
    while (total_size != 0)
    {
        /* explain the software para(mode) to the hardware para */
        mode = &request_p->control_mode;
        if (((*mode & CBC_MODE) || (*mode & CTR_MODE) || (*mode & CFB_MODE)
                || (*mode & OFB_MODE)) && (first_in == 0))
        {
            *mode |= 1 << 7;
            request_p->iv_flag = TRUE;
        }
        else
        {
            *mode &= ~(1 << 7);
            request_p->iv_flag = FALSE;
        }
        if (first_in == 0)
            fh_set_aes_key_reg(obj_p, request_p);
        fh_aes_update_request_data_size(request_p, obj_p->otp_size, &total_size, &first_in);
        /* emode & method */
        /* iv & key already get... */
        /* fifo threshold .. */
        outfifo_thold = 0;
        infifo_thold = 8;
        //isr = p_driver->irq_en;
        /* here set the hardware reg.....lock now.... */
        /* set key... */
        p_driver->cur_request = request_p;
        p_driver->cur_crypto = obj_p;
        AES_DBG("control_reg:0x%x\n", *mode);
        aes_writel(p_driver, encrypt_control, *mode);
        fh_set_indata(obj_p, request_p);
        fh_set_outdata(obj_p,request_p);
        //set hw para...
        fh_set_dma_indata(obj_p,request_p);
        fh_set_dma_outdata(obj_p,request_p);

        //set fifo..
        AES_DBG("outfifo thold:%x\n", outfifo_thold);
        AES_DBG("infifo thold:%x\n", infifo_thold);
        aes_writel(p_driver, fifo_threshold, outfifo_thold << 8 | infifo_thold);

        //set isr..
        //close all the isr...
        aes_writel(p_driver, intr_enable, 0);

        //enable dma go..
        aes_writel(p_driver, dma_control, 1);

        do{
            isr_status = aes_readl(p_driver, intr_src);
        }while(!(isr_status & 0x01));


        fh_unset_indata(obj_p,request_p);
        fh_unset_outdata(obj_p,request_p);
        fh_aes_update_request_data_add(request_p);
    }
}

static int fh_aes_set_key(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p)
{
    return 0;
}

#ifdef CONFIG_CRYPTO_AES_ECB_SUPPORT
static int fh_aes_encrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p)
{

    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *) obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n", __func__);
        ASSERT(0)
        ;
        break;
    }
    p_driver->control_mode |= ECB_MODE | ENCRYPT;
    request_p->control_mode |= ECB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p, request_p);
    return 0;

}

static int fh_aes_decrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p)
{

    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *) obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n", __func__);
        ASSERT(0)
        ;
    }
    p_driver->control_mode |= ECB_MODE | DECRYPT;
    request_p->control_mode |= ECB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p, request_p);

    return 0;

}
#endif

#ifdef CONFIG_CRYPTO_AES_CBC_SUPPORT
static int fh_aes_cbc_encrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p)
{

    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *) obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n", __func__);
        ASSERT(0)
        ;
        break;
    }
    p_driver->control_mode |= CBC_MODE | ENCRYPT;
    request_p->control_mode |= CBC_MODE | ENCRYPT;

    fh_aes_handle_request(obj_p, request_p);
    return 0;

}

static int fh_aes_cbc_decrypt(struct rt_crypto_obj *obj_p, struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *) obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n", __func__);
        ASSERT(0)
        ;
        break;
    }
    p_driver->control_mode |= CBC_MODE | DECRYPT;
    request_p->control_mode |= CBC_MODE | DECRYPT;
    fh_aes_handle_request(obj_p, request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_AES_CTR_SUPPORT
static int fh_aes_ctr_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n",__func__);
        ASSERT(0);
        break;
    }
    p_driver->control_mode |= CTR_MODE | ENCRYPT;
    request_p->control_mode |= CTR_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_aes_ctr_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n",__func__);
        ASSERT(0);
        break;
    }
    p_driver->control_mode |= CTR_MODE | DECRYPT;
    request_p->control_mode |= CTR_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_AES_OFB_SUPPORT
static int fh_aes_ofb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n",__func__);
        ASSERT(0);
        break;
    }
    p_driver->control_mode |= OFB_MODE | ENCRYPT;
    request_p->control_mode |= OFB_MODE | ENCRYPT;

    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_aes_ofb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n",__func__);
        ASSERT(0);
        break;
    }
    p_driver->control_mode |= OFB_MODE | DECRYPT;
    request_p->control_mode |= OFB_MODE | DECRYPT;

    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_AES_CFB_SUPPORT
static int fh_aes_cfb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n",__func__);
        ASSERT(0);
        break;
    }
    p_driver->control_mode |= CFB_MODE | ENCRYPT;
    request_p->control_mode |= CFB_MODE | ENCRYPT;

    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_aes_cfb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;

    switch (request_p->key_size)
    {
    case AES_KEYSIZE_128:
        p_driver->control_mode = AES_128_METHOD;
        request_p->control_mode = AES_128_METHOD;
        break;
    case AES_KEYSIZE_192:
        p_driver->control_mode = AES_192_METHOD;
        request_p->control_mode = AES_192_METHOD;
        break;
    case AES_KEYSIZE_256:
        p_driver->control_mode = AES_256_METHOD;
        request_p->control_mode = AES_256_METHOD;
        break;
    default:
        AES_ERR("%s wrong key size..\n",__func__);
        ASSERT(0);
        break;
    }
    p_driver->control_mode |= CFB_MODE | DECRYPT;
    request_p->control_mode |= CFB_MODE | DECRYPT;

    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_DES_ECB_SUPPORT
static int fh_des_ecb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;
    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= ECB_MODE | ENCRYPT;
    request_p->control_mode |= ECB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_des_ecb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= ECB_MODE | DECRYPT;
    request_p->control_mode |= ECB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif
#ifdef CONFIG_CRYPTO_DES_CBC_SUPPORT
static int fh_des_cbc_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= CBC_MODE | ENCRYPT;
    request_p->control_mode |= CBC_MODE | ENCRYPT;

    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_des_cbc_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= CBC_MODE | DECRYPT;
    request_p->control_mode |= CBC_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_DES_OFB_SUPPORT
static int fh_des_ofb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= OFB_MODE | ENCRYPT;
    request_p->control_mode |= OFB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_des_ofb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= OFB_MODE | DECRYPT;
    request_p->control_mode |= OFB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_DES_CFB_SUPPORT
static int fh_des_cfb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= CFB_MODE | ENCRYPT;
    request_p->control_mode |= CFB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_des_cfb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = DES_METHOD;
    request_p->control_mode = DES_METHOD;
    p_driver->control_mode |= CFB_MODE | DECRYPT;
    request_p->control_mode |= CFB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

#ifdef CONFIG_CRYPTO_DES_TRI_ECB_SUPPORT
static int fh_des_tri_ecb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= ECB_MODE | ENCRYPT;
    request_p->control_mode |= ECB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}


static int fh_des_tri_ecb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= ECB_MODE | DECRYPT;
    request_p->control_mode |= ECB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_CBC_SUPPORT
static int fh_des_tri_cbc_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= CBC_MODE | ENCRYPT;
    request_p->control_mode |= CBC_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;

}


static int fh_des_tri_cbc_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= CBC_MODE | DECRYPT;
    request_p->control_mode |= CBC_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_OFB_SUPPORT
static int fh_des_tri_ofb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= OFB_MODE | ENCRYPT;
    request_p->control_mode |= OFB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;

}


static int fh_des_tri_ofb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= OFB_MODE | DECRYPT;
    request_p->control_mode |= OFB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif
#ifdef CONFIG_CRYPTO_DES_TRI_CFB_SUPPORT
static int fh_des_tri_cfb_encrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= CFB_MODE | ENCRYPT;
    request_p->control_mode |= CFB_MODE | ENCRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;

}


static int fh_des_tri_cfb_decrypt(struct rt_crypto_obj *obj_p,struct rt_crypto_request *request_p)
{
    struct fh_aes_driver *p_driver;

    p_driver = (struct fh_aes_driver *)obj_p->driver_private;
    p_driver->control_mode = TRIPLE_DES_METHOD;
    request_p->control_mode = TRIPLE_DES_METHOD;
    p_driver->control_mode |= CFB_MODE | DECRYPT;
    request_p->control_mode |= CFB_MODE | DECRYPT;
    fh_aes_handle_request(obj_p,request_p);
    return 0;
}
#endif

int fh_aes_probe(void){

    struct fh_aes_driver *fh_aes_driver_obj;
    struct rt_crypto_obj *p_crypto;
    u32 i;
    /* malloc data */
    fh_aes_driver_obj = &g_aes_driver;//(struct fh_aes_driver *) rt_malloc(sizeof(struct fh_aes_driver));
    fh_aes_driver_obj->open_flag = AES_SUPPORT_FLAG;
    if (fh_aes_driver_obj->open_flag & CRYPTO_EX_MEM_SET_KEY)
    {
       fh_aes_driver_obj->p_efuse =  &s_efuse_obj;//rt_malloc(sizeof(struct wrap_efuse_obj));
       fh_aes_driver_obj->p_efuse->regs = (void *)EFUSE_REG_BASE;
       s_efuse_obj.regs = (void *) EFUSE_REG_BASE;
       if (fh_aes_driver_obj->open_flag & CRYPTO_EX_MEM_INDEP_POWER)
       {
           /*efuse power up*/
           open_efuse_power(fh_aes_driver_obj->p_efuse);
       }

    }
#ifdef EFUSE_BUFFER_OFF
    efuse_buffer_set(fh_aes_driver_obj->p_efuse, 0);
#endif
    fh_aes_driver_obj->id = 0;
    fh_aes_driver_obj->irq_no = 0;
    fh_aes_driver_obj->regs = (void *)AES_REG_BASE;


    AES_DBG("id:%d, irq:%d, regs:%x..\n", fh_aes_driver_obj->id, fh_aes_driver_obj->irq_no,
            (u32)fh_aes_driver_obj->regs);
    //fh_aes_driver_obj->irq_en = 1 << 0;

	//register the algo ....
	for(i = 0, p_crypto = &crypto_table[0];i<ARRAY_SIZE(crypto_table);i++,p_crypto++){
        printf("crypto name is [%s]\n",p_crypto->name);
		rt_algo_register(RT_Algo_Class_Crypto,p_crypto,p_crypto->name,(void*)fh_aes_driver_obj);
	}


    // efuse_devid_write_read();
    // efuse_key_write();
    // efuse_lock();
    // aes_crypto_with_cpu_or_efuse();
	return 0;
}




/* efuse driver.... */
int efuse_detect_complete(struct wrap_efuse_obj *obj, int pos)
{
    unsigned int rdata;
    unsigned int time = 0;
    /* printf("efuse wait pos %x...\n",pos); */
    do
    {
        time++;
        rdata = wrap_readl(obj, efuse_status0);
        if (time > EFUSE_TIMEOUT)
        {
            printf("[efuse]:detect time out...pos: 0x%x\n", pos);
            return -1;
        }

    } while ((rdata & (1 << pos)) != 1 << pos);
    return 0;
    /* printf("efuse wait pos done...\n",pos); */
}



void efuse_dump_register(struct wrap_efuse_obj *obj){
	printf("cmd      :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_cmd));
	printf("config   :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_config));
	printf("match_key:\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_match_key));
	printf("timing0  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing0));
	printf("timing1  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing1));
	printf("timing2  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing2));
	printf("timing3  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing3));
	printf("timing4  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing4));
	printf("timing5  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing5));
	printf("timing6  :\t%08x ...SW:WR, HW:RO\n",wrap_readl(obj,efuse_timing6));
	printf("dout     :\t%08x ...SW:RO, HW:WR\n",wrap_readl(obj,efuse_dout));
	printf("status0  :\t%08x ...SW:RO, HW:WO\n",wrap_readl(obj,efuse_status0));
	printf("status1  :\t%08x ...SW:RO, HW:WO\n",wrap_readl(obj,efuse_status1));
	printf("status2  :\t%08x ...SW:RO, HW:WO\n",wrap_readl(obj,efuse_status2));
	printf("status3  :\t%08x ...SW:RO, HW:WO\n",wrap_readl(obj,efuse_status3));
	printf("status4  :\t%08x ...SW:RO, HW:WO\n",wrap_readl(obj,efuse_status4));

}

void open_efuse_power(struct wrap_efuse_obj *obj)
{
    unsigned int data;
    data = wrap_readl(obj, efuse_config);
    data |= 1 << 27;
    data = wrap_writel(obj, efuse_config, data);
}

void efuse_buffer_set(struct wrap_efuse_obj *obj, unsigned int value)
{
    unsigned int data;
    data = wrap_readl(obj, efuse_config);
    data &= ~(1 << 4);
    data |= value << 4;
    data = wrap_writel(obj, efuse_config, data);
}

void auto_check_efuse_pro_bits(struct wrap_efuse_obj *obj, u32 *buff)
{
    buff[0] = wrap_readl(obj, efuse_status1);
    buff[1] = wrap_readl(obj, efuse_status2);
}

void efuse_write_key_byte(struct wrap_efuse_obj *obj, u32 entry, u8 data)
{

    u32 temp = 0;

    temp = (u32) data;
    /* 0~255 */
    temp &= ~0xffffff00;
    temp <<= 12;
    /* 0~63 */
    entry &= 0x3f;
    temp |= (entry << 4) | (0x02);

    wrap_writel(obj, efuse_cmd, temp);
    efuse_detect_complete(obj, 2);
}
void efuse_write_matchkey(struct wrap_efuse_obj *obj, u32 matchkey)
{

    wrap_writel(obj, efuse_match_key, matchkey);
}
u32 efuse_read_matchkey(struct wrap_efuse_obj *obj)
{

    return wrap_readl(obj, efuse_match_key);
}



void efuse_load_usrcmd(struct wrap_efuse_obj *obj)
{

    wrap_writel(obj, efuse_cmd, 1);
    efuse_detect_complete(obj, 1);

}

void auto_check_err_bits(struct wrap_efuse_obj *obj, u32 *buff)
{

    /* first set auto check cmd */
    u32 data;
    /* efuse_load_usrcmd(obj); */
    data = wrap_readl(obj, efuse_status3);
    *buff = data;

}

void efuse_check_map_para(unsigned int size, struct ex_key_map *p_key_map){
    int loop;
    ASSERT(size <= MAX_EX_KEY_MAP_SIZE);
    for (loop = 0; loop < size; loop++) {
        if ((p_key_map[loop].ex_mem_entry % 4 != 0)
                || (p_key_map[loop].crypto_key_no != loop)) {
            AES_ERR("map[%d]:entry[0x%x]:aes key[0x%x] para error..\n", loop,
                    p_key_map[loop].ex_mem_entry,
                    p_key_map[loop].crypto_key_no);
            ASSERT(0);
        }
    }
}

void efuse_trans_key(struct fh_aes_driver *p_driver,struct rt_crypto_request *request_p)
{
    int i;
    struct wrap_efuse_obj *obj;
    struct ex_key_map *p_key_map;
    int key_map_size;
    unsigned int temp_reg;
    obj = p_driver->p_efuse;
    if(p_driver->open_flag & CRYPTO_EX_MEM_SWITCH_KEY){
        /*chip need set the map...and usr set ~~~*/
        if(request_p->key_flag & CRYPTO_EX_MEM_SWITCH_KEY){
            //parse efuse map..
            if(request_p->key_flag & CRYPTO_EX_MEM_4_ENTRY_1_KEY){
                AES_DBG("parse efuse map...map size: %d\n",request_p->adv.ex_key_para.map_size);
                key_map_size = request_p->adv.ex_key_para.map_size;
                p_key_map = &request_p->adv.ex_key_para.map[0];
                //check map buf data...
                efuse_check_map_para(key_map_size,p_key_map);
                for (i = 0; i < key_map_size; i++,p_key_map++)
                {
                    refresh_efuse(obj);
                    temp_reg = wrap_readl(obj,efuse_config);
                    temp_reg &= ~(0xf <<28);
                    temp_reg |= (p_key_map->ex_mem_entry / 4) << 28;
                    wrap_writel(obj, efuse_config, temp_reg);
                    wrap_writel(obj, efuse_cmd, (i << 20) + 0x04);
                    efuse_detect_complete(obj, 4);
                }
            }
        }
        else{
            /*chip need set the map...and usr not set ~~~*/
            AES_DBG("usr not set efuse map...key size:%d\n",request_p->key_size);
            key_map_size = request_p->key_size;
            for (i = 0; i < key_map_size / 4; i++)
            {
                refresh_efuse(obj);
                temp_reg = wrap_readl(obj,efuse_config);
                temp_reg &= ~(0xf <<28);
                temp_reg |= i << 28;
                wrap_writel(obj, efuse_config, temp_reg);
                wrap_writel(obj, efuse_cmd, (i << 20) + 0x04);
                efuse_detect_complete(obj, 4);
            }
        }
    }
    else{
        AES_DBG("compatible LINBAO & DUOBAO\n");
        key_map_size = request_p->key_size;
        for (i = 0; i < key_map_size / 4; i++)
        {
            refresh_efuse(obj);
            wrap_writel(obj, efuse_cmd, (i << 20) + 0x04);
            efuse_detect_complete(obj, 4);
        }
    }
}

void efuse_get_lock_status(struct wrap_efuse_obj *obj, struct efuse_status *status)
{
    status->efuse_apb_lock = (wrap_readl(obj, efuse_status0) >> 20) & 0x0f;
    status->aes_ahb_lock = (wrap_readl(obj, efuse_status0) >> 24) & 0x0f;
}

void efuse_read_entry(struct wrap_efuse_obj *obj, u32 key, u32 start_entry, u8 *buff,
        u32 size)
{
    u32 data, i;
    for (i = 0; i < size; i++)
    {
        wrap_writel(obj, efuse_match_key, key);
        wrap_writel(obj, efuse_cmd, ((start_entry + i) << 4) + 0x03);
        efuse_detect_complete(obj, 3);
        data = wrap_readl(obj, efuse_dout);
        *buff++ = (u8) data;
    }
}

void efuse_write_cmd(struct wrap_efuse_obj *obj, u32 data)
{
    wrap_writel(obj, efuse_cmd, data);
}

void refresh_efuse(struct wrap_efuse_obj *obj)
{
    wrap_writel(obj, efuse_cmd, CMD_WFLGA_AUTO);
    efuse_detect_complete(obj, 8);
    efuse_load_usrcmd(obj);
}

long fh_efuse_ioctl(EFUSE_INFO *efuse_user_info, unsigned int cmd,
        unsigned long arg)
{

    int i;
    EFUSE_INFO efuse_info = { 0 };

    u32 temp_swap_data[32] = { 0 };
    u32 *p_dst;
    u8 *p_dst_8;
    unsigned int data;

    memcpy(&efuse_info, efuse_user_info, sizeof(EFUSE_INFO));
    if (s_efuse_obj.regs == NULL)
    {
        printf("please init efuse first..\n");
        return -1;
    }
    refresh_efuse(&s_efuse_obj);

    switch (cmd)
    {
    case IOCTL_EFUSE_CHECK_PRO:
        auto_check_efuse_pro_bits(&s_efuse_obj, efuse_info.status.protect_bits);
        break;

    case IOCTL_EFUSE_WRITE_KEY:
        memcpy((u8 *) &temp_swap_data[0], efuse_info.key_buff, efuse_info.key_size);
        p_dst = &temp_swap_data[0];
        for (i = 0; i < efuse_info.key_size / sizeof(u32); i++)
        {
            aes_biglittle_swap((u8 *) (p_dst + i));
        }
        p_dst_8 = (u8 *) &temp_swap_data[0];

        for (i = 0; i < efuse_info.key_size; i++)
        {
            efuse_write_key_byte(&s_efuse_obj, efuse_info.efuse_entry_no + i, *(p_dst_8 + i));
        }
        break;
	case IOCTL_EFUSE_READ_KEY:
		efuse_read_entry(&s_efuse_obj, efuse_info.status.error,
		efuse_info.efuse_entry_no,
		efuse_info.key_buff, efuse_info.key_size);
		p_dst = (u32 *)efuse_info.key_buff;
		for (i = 0; i < efuse_info.key_size / sizeof(u32); i++) {
			aes_biglittle_swap((u8 *) (p_dst + i));
			/*printk("swap data is %x\n",*(p_dst + i));*/
		}
		break;

    case IOCTL_EFUSE_CHECK_LOCK:
        efuse_get_lock_status(&s_efuse_obj, &efuse_info.status);
        break;
    case IOCTL_EFUSE_TRANS_KEY:
        printf("please use efuse transkey with aes...\n");
        break;

    case IOCTL_EFUSE_CHECK_ERROR:
        auto_check_err_bits(&s_efuse_obj, &efuse_info.status.error);
        break;
	case IOCTL_EFUSE_WRITE_ENTRY:
		for (i = 0; i < efuse_info.key_size; i++)
			efuse_write_key_byte(&s_efuse_obj, efuse_info.efuse_entry_no + i, efuse_info.key_buff[i]);
		break;
    case IOCTL_EFUSE_READ_ENTRY:
        /*printf("not support read efuse entry...\n");*/
    	efuse_read_entry(&s_efuse_obj, efuse_info.status.error,
		efuse_info.efuse_entry_no,
		efuse_info.key_buff, efuse_info.key_size);
        break;

    case IOCTL_EFUSE_SET_LOCK_DATA:
        /* parse lock data... */
        data = efuse_info.status.aes_ahb_lock;
        data <<= 4;
        data &= 0xf0;
        efuse_info.status.efuse_apb_lock &= 0x0f;
        data |= efuse_info.status.efuse_apb_lock;
        efuse_write_key_byte(&s_efuse_obj, 63, (u8) data);
        break;
    case IOCTL_EFUSE_GET_LOCK_DATA:
        printf("not support get lock data ...\n");
        break;
    case IOCTL_EFUSE_SET_MAP_PARA_4_TO_1:
        printf("please use with aes...\n");
         break;
    case IOCTL_EFUSE_SET_MAP_PARA_1_TO_1:
        printf("not support this func now..\n");
        break;
    case IOCTL_EFUSE_CLR_MAP_PARA:
        printf("not support here...\n");
        break;
    case IOCTL_EFUSE_DUMP_REGISTER:
        //printf("not support here...\n");
        efuse_dump_register(&s_efuse_obj);

        break;

    case IOCTL_EFUSE_DEBUG:
    {
        if (efuse_info.cmd != 2 && efuse_info.cmd != 3)  {
            unsigned int mk =efuse_read_matchkey(&s_efuse_obj);
            efuse_write_matchkey(&s_efuse_obj,0);
            data = 3;
            data |= efuse_info.addr <<4;
            data |= efuse_info.wvalue <<12;
            data |= efuse_info.aeskeyid <<20;
            efuse_write_cmd(&s_efuse_obj,data);
            efuse_write_matchkey(&s_efuse_obj,mk);
        } else {
            data = efuse_info.cmd;
            data |= efuse_info.addr <<4;
            data |= efuse_info.wvalue <<12;
            data |= efuse_info.aeskeyid <<20;
            efuse_write_cmd(&s_efuse_obj,data);
        }
        break;
    }
    default:
        break;
    }

    memcpy(&efuse_user_info->status, &efuse_info.status, sizeof(struct efuse_status));
    return 0;
}

void *get_efuse_handle(void){
    return (void *)&s_efuse_obj;
}


static int get_soc_crypto_load_mode_from_efuse(struct rt_crypto_obj *obj_p)
{
    struct wrap_efuse_obj *p_efuse;
    struct fh_aes_driver *p_driver;
    unsigned char ret_buf;

    p_efuse = (struct wrap_efuse_obj *)get_efuse_handle();
    p_driver = (struct fh_aes_driver *) obj_p->driver_private;
	refresh_efuse(p_efuse);
	efuse_read_entry(p_efuse, MATCH_KEY, SECURE_BOOT_ENTRY, &ret_buf, 1);
    if(ret_buf & SECURE_BOOT_ACTIVE_VAL)
        return SOC_BOOT_MODE_WITH_SECURE;
    return -1;
}

